﻿using System;
using System.Threading;
using System.Net;
using InnovTech.Data;

namespace InnovTech.Net.DataTransfer
{
    public abstract class DataTransferBase : IDataTransfer
    {

        protected static TimeSpan DefaultRetryInterval = new TimeSpan(0, 1, 0);

        protected DataTransferState _state = DataTransferState.None;
        public DataTransferState State
        {
            get
            {
                return _state;
            }
            protected set
            {
                if (_state != value)
                {
                    DataTransferState old = _state;
                    _state = value;
                    DataTransferStateChangedEventArgs args = new DataTransferStateChangedEventArgs(old, _state);
                    args.Error = CurrentError;
                    RaiseEvent<DataTransferStateChangedEventArgs>(this, StateChanged, args);
                }
            }
        }

        protected Exception CurrentError { get; set; }


        public bool TransferData(IDataTransferConfig config)
        {
            CurrentError = null;
            ResetEventHandlers(config);

            ErrorPolicy errorPolicy = config.ShouldRetry ? ErrorPolicy.Retry : ErrorPolicy.Close;

            State = DataTransferState.Initialized;
            DataTransferEventArgsBase eventArgs = new DataTransferInitializedEventArgs();
            RaiseEvent<DataTransferInitializedEventArgs>(this, TransferInitialized, eventArgs as DataTransferInitializedEventArgs);
            if (State == DataTransferState.Canceled)
                return false;

            try
            {

                State = DataTransferState.Connecting;
                if (!ConnectionUtil.GetIsInternetAvaliable())
                {
                    throw new WebException("No Internet Connection", WebExceptionStatus.ConnectFailure);
                }

                eventArgs = new DataTransferConnectingEventArgs();
                RaiseEvent<DataTransferConnectingEventArgs>(this, TransferConnecting, eventArgs as DataTransferConnectingEventArgs);
                if (State == DataTransferState.Canceled)
                    return false;

                Connect(config);

                State = DataTransferState.TransferStarting;
                eventArgs = new DataTransferStartingEventArgs();
                RaiseEvent<DataTransferStartingEventArgs>(this, TransferStarting, eventArgs as DataTransferStartingEventArgs);
                if (State == DataTransferState.Canceled)
                    return false;

                State = DataTransferState.Transfering;
                TransferDataCore(config);

                if (State == DataTransferState.Canceled)
                    return false;

                State = DataTransferState.TransferCompleted;
                RaiseCompletedEvent(new DataTransferCompletedEventArgs());
                return true;
            }
            catch (Exception ex)
            {
                CurrentError = ex;
                State = DataTransferState.Error;
                DataTransferErrorEventArgs errorArgs = new DataTransferErrorEventArgs(ex, errorPolicy, config.RetryInterval);
                RaiseErrorEvent(errorArgs);
                if (errorArgs.ErrorHandlerType == ErrorPolicy.Retry)
                {
                    Thread newThread = new Thread(new ThreadStart(
                        () =>
                        {
                            Thread.Sleep(errorArgs.RetryInterval);
                            TransferData(config);
                        }));
                    newThread.Start();
                }
                return false;
            }
        }

        private void CleanupEventHandlers()
        {
            TransferStarting = null;
            TransferInitialized = null;
            TransferConnecting = null;
            Transfering = null;
            TransferCanceled = null;
            TransferError = null;
            TransferCompleted = null;
            StateChanged = null;
        }

        private void SetEventHandlers(IDataTransferConfig config)
        {
            if (config.TransferStarting != null)
                TransferStarting += config.TransferStarting;
            if (config.TransferInitialized != null)
                TransferInitialized += config.TransferInitialized;
            if (config.TransferConnecting != null)
                TransferConnecting += config.TransferConnecting;
            if (config.Transfering != null)
                Transfering += config.Transfering;
            if (config.TransferCanceled != null)
                TransferCanceled += config.TransferCanceled;
            if (config.TransferError != null)
                TransferError += config.TransferError;
            if (config.TransferCompleted != null)
                TransferCompleted += config.TransferCompleted;
            if (config.StateChanged != null)
                StateChanged += config.StateChanged;
        }

        private void ResetEventHandlers(IDataTransferConfig config)
        {
            CleanupEventHandlers();
            SetEventHandlers(config);
        }

        protected abstract void TransferDataCore(IDataTransferConfig config);
        protected abstract void Connect(IDataTransferConfig config);

        #region protected

        protected void HandleCanceledEvent(DataTransferEventArgsBase args)
        {
            if (args.Cancel)
            {
                State = DataTransferState.Canceled;
                RaiseCanceledEvent(new DataTransferCanceledEventArgs());
            }
        }

        protected void RaiseEvent<T>(object sender, EventHandler<T> handler, T args) where T : DataTransferEventArgsBase
        {
            if (handler != null)
                handler(sender, args);
            if (args != null)
                HandleCanceledEvent(args);
        }

        protected void RaiseTransferingEvent(DataTransferingEventArgs args)
        {
            RaiseEvent<DataTransferingEventArgs>(this, Transfering, args);
        }

        protected void RaiseCompletedEvent(DataTransferCompletedEventArgs args)
        {
            RaiseEvent<DataTransferCompletedEventArgs>(this, TransferCompleted, args);
        }

        protected void RaiseCanceledEvent(DataTransferCanceledEventArgs args)
        {
            RaiseEvent<DataTransferCanceledEventArgs>(this, TransferCanceled, args);
        }

        protected void RaiseErrorEvent(DataTransferErrorEventArgs args)
        {
            RaiseEvent<DataTransferErrorEventArgs>(this, TransferError, args);
        }

        #endregion protected

        #region events
        public event EventHandler<DataTransferStartingEventArgs> TransferStarting;
        public event EventHandler<DataTransferInitializedEventArgs> TransferInitialized;
        public event EventHandler<DataTransferConnectingEventArgs> TransferConnecting;
        public event EventHandler<DataTransferingEventArgs> Transfering;
        public event EventHandler<DataTransferCanceledEventArgs> TransferCanceled;
        public event EventHandler<DataTransferErrorEventArgs> TransferError;
        public event EventHandler<DataTransferCompletedEventArgs> TransferCompleted;

        public event EventHandler<DataTransferStateChangedEventArgs> StateChanged;
        #endregion events






    }
}
